#!/usr/bin/env python
# -*- coding: utf-8 -*-
###########################################################################
#   Copyright (C) 2008 by Serj Poltavsky                                  #
#   serge.uliss at gmail.com                                              #
#                                                                         #
#   This program is free software; you can redistribute it and/or modify  #
#   it under the terms of the GNU General Public License as published by  #
#   the Free Software Foundation; either version 3 of the License, or     #
#   (at your option) any later version.                                   #
#                                                                         #
#   This program is distributed in the hope that it will be useful,       #
#   but WITHOUT ANY WARRANTY; without even the implied warranty of        #
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
#   GNU General Public License for more details.                          #
#                                                                         #
#   You should have received a copy of the GNU General Public License     #
#   along with this program. If not, see <http://www.gnu.org/licenses/>   #
###########################################################################

import os
import sys
from xml.dom import minidom
from node import Node

class DvdAuthor(Node):
    ''' Creates DvdAuthor object. If filename given tries to load it '''
    def __init__(self, destination = 'DVD'):
        Node.__init__(self, 'dvdauthor')
        self.destination = destination
        self.__dom = None
        self.vmgm = VMGM()

    def __str__(self):
        return self.dom.toprettyxml(encoding="utf-8")

    ''' Adds empty title to the end of project '''
    def append(self, title):
        if not isinstance(title, TitleSet):
            raise TypeError('TitleSet object expected')
        if len(self) >= 99:
            raise RuntimeError("dvd can contain no more than 99 titles")
        list.append(self, title)

    ''' Returns desination directory, where dvd will create files '''
    def get_destination(self):
        return self.__destination

    ''' Returns XML DOM '''
    def get_dom(self):
        return self.__dom

    ''' Saves DvdAuthor xml file '''
    def save(self, filename):
        f = open(filename, "w", 1)
        f.write(self.dom.toprettyxml(encoding="utf-8"))
        f.close()

    def set_destination(self, value):
         self.__destination = value

    ''' Updates dvdauthor '''
    def update(self):
        self.__dom = minidom.Document()
        root = Node.update(self, self.__dom, self.__dom)
        root.setAttribute('dest', self.destination)
        self.update_vmgm(self.__dom, root)
        self.update_titles(self.__dom, root)
        return root

    def update_titles(self, dom, root):
        for title in self:
            title.update(dom, root)

    def update_vmgm(self, dom, root):
        self.vmgm.update(dom, root)

    dom = property(get_dom)
    destination = property(get_destination, set_destination)

class Audio(Node):
    def __init__(self, dvdauthor, fname):
        Node.__init__(self, dvdauthor, "audio")
        self.__format = None
        self.__quant = None
        self.__surround = False
        self.__channels = None
        self.__samplerate = None

class Button(Node):
    def __init__(self, name):
        Node.__init__(self, "button")
        self.name = name

    def update(self, dom, parent):
        button = Node.update(self, dom, parent)
        if self.name is not None:
            button.setAttribute('name', self.name)
        if len(self):
            text = dom.createTextNode(" ".join(self))
            button.appendChild(text)
        return button

class FPC(Node):
    def __init__(self, value = None):
        Node.__init__(self, "fpc")
        self.value = value

    def get_value(self):
        return self.__value

    def set_value(self, value):
        self.__value = value

    def update(self, dom, parent):
        fpc = Node.update(self, dom, parent)
        if self.value:
            txt = dom.createTextNode(str(self.value))
            fpc.appendChild(txt)
        return fpc

    value = property(get_value, set_value)

class Menu(Node):
    def __init__(self):
        Node.__init__(self, "menus")
        self.video = None
        self.audio = None

    def append(self, pgc):
        if isinstance(pgc, PGC):
            list.append(self, pgc)
        else:
            raise TypeError('PGC instance expected')

    def append_audio(self, audio):
        if isinstance(audio, Audio):
            self.__audio.append(audio)
        else:
            raise TypeError("Not a Audio instance given")

    def get_audio(self):
        return self.__audio

    def get_video(self):
        return self.__video

    def set_audio(self, audio):
        if isinstance(audio, Audio):
            self.__audio = [audio]
        elif audio is None:
            self.__audio = []
        else:
            raise TypeError("Not a Audio instance given")

    def set_video(self, video):
        if video is None or isinstance(video, Video):
            self.__video = video
        else:
            raise TypeError("Not a Video instance given")

    def update(self, dom, parent):
        menu = Node.update(self, dom, parent)
        self.update_video(dom, menu)
        self.update_audio(dom, menu)
        self.update_pgc(dom, menu)
        return menu

    def update_audio(self, dom, menu):
        pass

    def update_pgc(self, dom, menu):
        for pgc in self:
            pgc.update(dom, menu)

    def update_video(self, dom, menu):
        if self.video is not None:
            self.video.update(dom, menu)

    video = property(get_video, set_video)

class Pauseable:
    def __init__(self, pause):
        self.pause = pause

    def get_pause(self):
        return self.__pause

    def set_pause(self, value):
        if value == "inf" or (int(value) >= 0 and int(value) < 255):
            self.__pause = str(value)
        else:
            raise ValueError(value)

    pause = property(get_pause, set_pause)

class PGC(Node, Pauseable):
    def __init__(self, vob = None):
        Node.__init__(self, "pgc")
        Pauseable.__init__(self, 0)
        if vob is not None:
            self.append(vob)
        self.__pre_action = ""
        self.__post_action = ""
        self.__entry = ""
        self.__palette = ""

    def append(self, value):
        if isinstance(value, VOB) or isinstance(value, Button):
            list.append(self, value)
        else:
            raise TypeError('VOB or Button instance expected')

    def get_entry(self):
        return self.__entry

    def get_palette(self):
        return self.__palette

    def get_pre_action(self):
        return self.__pre_action

    def get_post_action(self):
        return self.__post_action

    def set_entry(self, entry):
        self.__entry = entry

    def set_palette(self, palette):
        if palette != "rgbfile" or palette != "yuvfile":
            raise ValueError(value)
        self.__palette = palette

    def set_pre_action(self, pre_action):
        self.__pre_action = pre_action

    def set_post_action(self, post_action):
        self.__post_action = post_action

    def update(self, dom, parent):
        pgc = Node.update(self, dom, parent)
        if self.palette:
            pgc.setAttribute("palette", self.palette)
        if self.pause != "0":
            pgc.setAttribute("pause", str(self.pause))
        if self.entry:
            pgc.setAttribute('entry', self.entry)

        self.update_pre_actions(dom, pgc)
        self.update_children(dom, pgc)
        self.update_post_actions(dom, pgc)
        return pgc

    def update_post_actions(self, dom, pgc):
        if self.post_action:
            post = dom.createElement("post")
            post.appendChild(dom.createTextNode(self.post_action))
            pgc.appendChild(post)

    def update_pre_actions(self, dom, pgc):
        if self.pre_action:
            pre = dom.createElement("pre")
            pre.appendChild(dom.createTextNode(self.pre_action))
            pgc.appendChild(pre)

    entry = property(get_entry, set_entry)
    palette = property(get_palette, set_palette)
    post_action = property(get_post_action, set_post_action)
    pre_action = property(get_pre_action, set_pre_action)

class TitleSet(Node):
    def __init__(self):
        Node.__init__(self, "titleset")
        self.__menu = None
        self.append(PGC())

    def append(self, pgc):
        if isinstance(pgc, PGC):
            Node.append(self, pgc)
        else:
            raise TypeError('PGC instance expected')

    def get_menu(self):
        return self.__menu

    def set_menu(self, menu):
        if isinstance(menu, Menu):
            self.__menu = menu
        else:
            raise TypeError('Menu instance expected')

    def update(self, dom, parent):
        titleset = Node.update(self, dom, parent)
        self.update_menu(dom, titleset)
        self.update_titles(dom, titleset)
        return titleset

    def update_menu(self, dom, parent):
        if self.menu is not None:
            self.menu.update(dom, parent)

    def update_titles(self, dom, parent):
        titles = dom.createElement("titles")
        parent.appendChild(titles)
        self.update_children(dom, titles)

    menu = property(get_menu,set_menu)

class Video(Node):
    def __init__(self, format="pal", aspect=(4, 3), widescreen=None):
        Node.__init__(self, "video")
        self.set_format(format)
        self.set_aspect(aspect)
        self.set_widescreen(widescreen)

    def get_aspect(self):
        return self.__aspect

    def get_format(self):
        return self.__format

    def get_widescreen(self):
        return self.__widescreen

    def set_aspect(self, aspect):
        if aspect is None:
            self.__aspect = None
        elif tuple(aspect) in ((4, 3), (16, 9)):
            self.__aspect = tuple(aspect)
        else:
            raise ValueError("Wrong aspect ratio: %s" % str(aspect))

    def set_format(self, format):
        if format in (None, "pal", "ntsc"):
            self.__format = format
        else:
            raise ValueError("Wrong video format: %s" % format)

    def update(self):
        if self.format:
            self.node.setAttribute("format", self.format)
        if self.aspect:
            self.node.setAttribute("aspect", ":".join(str(x) for x in self.aspect))
        if self.widescreen:
            self.node.setAttribute("widescreen", self.widescreen)

    def set_widescreen(self, widescreen):
        if widescreen in (None, "nopanscan", "crop", "noletterbox"):
            self.__widescreen = widescreen
        else:
            raise ValueError("Wrong widescreen value: %s" % widescreen)

    aspect = property(get_aspect, set_aspect)
    format = property(get_format, set_format)
    widescreen = property(get_widescreen, set_widescreen)

class VMGM(Node):
    def __init__(self):
        Node.__init__(self, 'vmgm')
        self.__menu = None
        self.__fpc = None

    def get_fpc(self):
        return self.__fpc

    def get_menu(self):
       return self.__menu

    def set_fpc(self, value):
        if value is None or isinstance(value, FPC):
            self.__fpc = value
        else:
            raise TypeError('FPC instance expected')

    def set_menu(self, menu):
        if menu is None or isinstance(menu, Menu):
            self.__menu = menu
        else:
            raise TypeError('Menu instance expected')

    def update(self, dom, root):
        vmgm = Node.update(self, dom, root)
        self.update_fpc(dom, vmgm)
        self.update_menu(dom, vmgm)
        return vmgm

    def update_fpc(self, dom, menus):
        if self.fpc is not None:
            self.fpc.update(dom, menus)

    def update_menu(self, dom, menus):
        if self.menu is not None:
            self.menu.update(dom, menus)

    fpc = property(get_fpc, set_fpc)
    menu = property(get_menu, set_menu)

class VOB(Node, Pauseable):
    def __init__(self, vobfile):
        Node.__init__(self, "vob")
        Pauseable.__init__(self, 0)
        self.chapters = []
        self.set_file(vobfile)

    def get_file(self):
        return self.__file

    def set_file(self, vobfile):
        if not os.path.exists(vobfile):
            raise RuntimeError("File not exists: %s" % vobfile)
        sz = os.path.getsize(vobfile)
        if sz > 1024 ** 3:
            raise RuntimeError("Filesize of \"%s\" is too big: %iMb; Maximum value is 1Gb" % (vobfile, sz/1024**2))
        self.__file = vobfile

    def update(self, dom, parent):
        vob = Node.update(self, dom, parent)
        if self.__file:
            vob.setAttribute("file", self.__file)
        if self.pause != "0":
            vob.setAttribute("pause", self.pause)
        return vob

    filename = property(get_file, set_file)